package com.nurun.cacheimageloader;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ContentHandler;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLConnection;

import android.content.Context;
import android.graphics.Bitmap;

import com.nurun.cacheimageloader.util.FileUtils;
import com.nurun.cacheimageloader.util.ImageFileStreamDecoder;
import com.nurun.cacheimageloader.util.Md5ImageNameGenerator;

public class CacheContentHandler extends ContentHandler {

    private long internalCacheSizeLimit = 1024 * 1024; // 1MB
    private long externalCacheSizeLimit = internalCacheSizeLimit * 25; //25MB
    private final Context mContext;
    private AbstractDiskCacheLimited mCache;

    public CacheContentHandler(Context context, long internalCacheSize, long externalCacheSize) {
        this.mContext = context;
        this.internalCacheSizeLimit = internalCacheSize;
        this.externalCacheSizeLimit = externalCacheSize;
    }
    
    /**
     * Create a new CacheContentHandler with default values
     * 
     * Default internalCacheSize = 1MB
     * Default externalCacheSize = 25MB
     * 
     * @param context
     */
    public CacheContentHandler(Context context) {
        this.mContext = context;

        File cacheDir = context.getExternalCacheDir();
        long cacheSizeLimit = externalCacheSizeLimit;

        if (cacheDir == null) {
            cacheDir = context.getCacheDir();
            cacheSizeLimit = internalCacheSizeLimit;
        }

        if (cacheDir != null) {
            mCache = new DiskCacheLimitedImpl(cacheDir, new Md5ImageNameGenerator(), cacheSizeLimit);
        }
    }

    @Override
    public Object getContent(URLConnection uConn) throws IOException {
        this.mContext.getApplicationContext();

        File imageFile = null;

        if (mCache != null) {
            imageFile = mCache.get(new Md5ImageNameGenerator().generate(uConn.getURL().toString()));
        }

        try {
            return loadBitmap(imageFile, uConn.getURL().toURI(), mCache);
        } catch (URISyntaxException e) {
            e.printStackTrace();
            throw new IllegalArgumentException("Invalid URI " + uConn.getURL());
        }
    }

    private Bitmap loadBitmap(File imageFile, URI remoteFileUri, DiskCacheInterface diskCache) throws IOException,
            URISyntaxException {

        Bitmap bmp = null;
        
        if (imageFile != null) {
            ImageFileStreamDecoder decoder = new ImageFileStreamDecoder(imageFile.toURI(), new ImageDownloaderFromNetwork());

            // Try to load image from disc cache
            if (imageFile.exists()) {

                bmp = decoder.decode();

                if (bmp != null) {
                    return bmp;
                }
            }
        }
        
        bmp = cacheNewImage(imageFile, remoteFileUri, new ImageDownloaderFromNetwork());
        
        if (diskCache != null) {
            diskCache.put(remoteFileUri.toString(), imageFile);
        }

        return bmp;
    }

    private Bitmap cacheNewImage(File targetFile, URI remoteUri, AbstractImageDownloader downloader) throws IOException,
            URISyntaxException {

        if (targetFile != null) {

            // Download, decode, compress and save image
            InputStream is = downloader.getStream(remoteUri);

            try {
                OutputStream os = new BufferedOutputStream(new FileOutputStream(targetFile));
                try {
                    FileUtils.copyStream(is, os);
                } finally {
                    os.close();
                }
            } finally {
                is.close();
            }

            ImageFileStreamDecoder decoder = new ImageFileStreamDecoder(targetFile.toURI(), downloader);
            return decoder.decode();
        } else {
            ImageFileStreamDecoder decoder = new ImageFileStreamDecoder(remoteUri, downloader);
            return decoder.decode();
        }

    }
}